package jp.co.nes.awssupport.access.dao;

import java.sql.Connection;
import java.sql.SQLException;
import java.time.YearMonth;
import java.util.List;

import org.apache.commons.dbutils.BasicRowProcessor;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jp.co.nes.awssupport.common.base.CommonException;
import jp.co.nes.awssupport.common.bean.AmountReportBean;
import jp.co.nes.awssupport.common.dao.BaseJdbcDao;
import jp.co.nes.awssupport.common.dao.BeanProcessorEx;
import jp.co.nes.awssupport.common.utility.DateUtil;
import jp.co.nes.awssupport.common.utility.DbConnUtil;

/**
 * 料金レポート用DAOです。
 */
public class AmountReportDao extends BaseJdbcDao<AmountReportBean> {

    /** ロガー */
    private static Logger logger = LoggerFactory.getLogger(AmountReportDao.class);

    /** 料金レポートのデータを取得するSelect文(タグ別料金レポート用・リソースID別料金レポート用) */
    private static final String SEL_REPORT_DATA = ""
            + "select #TARGET_COLUMN#,"
            + "       ProductName,"
            + "       sum(Rate * UsageQuantity) as Amount"
            + "   from daily_compiled_billing"
            + "  where LinkedAccountId = ?"
            + "    and YearMonth = ?"
            + "  group by #TARGET_COLUMN#, ProductName"
            + "  order by #TARGET_COLUMN#, ProductName";

    /** 料金レポートのデータを取得するSelect文(タグ・リソースID別料金レポート用) */
    private static final String SEL_REPORT_DATA_FOR_PER_TAGRESOURCE_ID = ""
            + "select NameTag,"
            + "       ResourceId,"
            + "       ProductName,"
            + "       Description,"
            + "       sum(UsageQuantity) as UsageQuantity,"
            + "       sum(Rate * UsageQuantity) as Amount"
            + "   from daily_compiled_billing"
            + "  where LinkedAccountId = ?"
            + "    and YearMonth = ?"
            + "  group by NameTag, ResourceId, ProductName, Description"
            + "  order by NameTag, ResourceId, ProductName, Description";

    /** 出力タイプ */
    private enum AmountReportType {
        PER_TAG, PER_RESOURCE_ID, PER_TAGRESOURCE_ID
    }

    /**
     * デフォルトコンストラクタです。
     */
    public AmountReportDao() {
    }

    /**
     * (タグ別)指定された連結アカウントIDの料金レポートのデータを返却します。<br>
     * データが存在しなかった場合は、空リストを返却します。
     *
     * @param linkedAccountId 連結アカウントID
     * @param yearMonth 年月
     * @return タグ別料金レポートデータ
     */
    public List<AmountReportBean> getReportDataPerTag(String linkedAccountId, YearMonth yearMonth) {
        return getReportData(linkedAccountId, yearMonth, AmountReportType.PER_TAG);
    }

    /**
     * (リソースID別)指定された連結アカウントIDの料金レポートのデータを返却します。<br>
     * データが存在しなかった場合は、空リストを返却します。
     *
     * @param linkedAccountId 連結アカウントID
     * @param yearMonth 年月
     * @return リソースID別料金レポートデータ
     */
    public List<AmountReportBean> getReportDataPerResourceId(String linkedAccountId, YearMonth yearMonth) {
        return getReportData(linkedAccountId, yearMonth, AmountReportType.PER_RESOURCE_ID);
    }

    /**
     * (タグ・リソースID別)指定された連結アカウントIDの料金レポートのデータを返却します。<br>
     * データが存在しなかった場合は、空リストを返却します。
     *
     * @param linkedAccountId 連結アカウントID
     * @param yearMonth 年月
     * @return タグ・リソースID別料金レポートデータ
     */
    public List<AmountReportBean> getReportDataPerTagResourceId(String linkedAccountId, YearMonth yearMonth) {
        return getReportData(linkedAccountId, yearMonth, AmountReportType.PER_TAGRESOURCE_ID);
    }

    /**
     * 指定された連結アカウントIDの料金レポートのデータを返却します。<br>
     * データが存在しなかった場合は、空リストを返却します。
     *
     * @param linkedAccountId 連結アカウントID
     * @param yearMonth 年月
     * @param type 出力タイプ
     * @return 料金レポートデータ
     */
    private static List<AmountReportBean> getReportData(String linkedAccountId, YearMonth yearMonth,
            AmountReportType type) {

        // SQL文を編集-集約項目に対して項目の置き換えを行う。
        String sSQL = null;
        if (type == AmountReportType.PER_TAGRESOURCE_ID) {
            sSQL = SEL_REPORT_DATA_FOR_PER_TAGRESOURCE_ID;
        } else {
            sSQL = SEL_REPORT_DATA;
            if (type == AmountReportType.PER_TAG) {
                sSQL = sSQL.replace("#TARGET_COLUMN#", "NameTag");
            } else if (type == AmountReportType.PER_RESOURCE_ID) {
                sSQL = sSQL.replace("#TARGET_COLUMN#", "ResourceId");
            } else {
                // 通常は発生しない。
                throw new CommonException("AmountReportType is wrong");
            }
        }

        if (logger.isDebugEnabled()) {
            logger.debug("SQL:" + sSQL);
        }

        // 料金レポートデータの取得
        try {
            Connection connection = DbConnUtil.getConn();

            if (logger.isDebugEnabled()) {
                logger.debug("linkedAccountId:" + linkedAccountId);
                logger.debug("yearMonth:" + yearMonth);
            }

            Object[] params = new Object[2];
            params[0] = linkedAccountId;
            params[1] = DateUtil.toInt(yearMonth);

            QueryRunner runner = new QueryRunner();
            return runner.query(connection, sSQL,
                    new BeanListHandler<>(AmountReportBean.class, new BasicRowProcessor(new BeanProcessorEx())),
                    params);
        } catch (SQLException e) {
            throw new CommonException(e);
        }
    }
}
